package com.jet.merchant.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jet.core.enums.CommonStatus;
import com.jet.core.enums.IEnum;
import com.jet.core.exception.CustomException;
import com.jet.core.model.vo.PageVo;
import com.jet.core.result.ResultCode;
import com.jet.core.service.RedisService;
import com.jet.core.utils.IdUtil;
import com.jet.core.utils.SecurityUtil;
import com.jet.merchant.domain.dto.AccountDto;
import com.jet.merchant.domain.dto.ClerkAccountRoleMapDto;
import com.jet.merchant.domain.dto.FirstSetPasswordDto;
import com.jet.merchant.domain.entity.*;
import com.jet.merchant.domain.enums.AccountCashMode;
import com.jet.merchant.domain.enums.RoleDefaultConst;
import com.jet.merchant.domain.query.AccountQuery;
import com.jet.merchant.domain.vo.*;
import com.jet.merchant.mapper.ClerkAccountMapper;
import com.jet.merchant.remote.cashier.MerchantCashierRemote;
import com.jet.merchant.remote.cashier.vo.CashierModeVo;
import com.jet.merchant.remote.office.MerchantOfficeRemote;
import com.jet.merchant.remote.office.vo.IndustryAttrVo;
import com.jet.merchant.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-02-25
 */
@Service
public class ClerkAccountServiceImpl extends ServiceImpl<ClerkAccountMapper, ClerkAccount> implements IClerkAccountService {

    private final IMerchantService merchantService;
    private final IMerchantStoreService merchantStoreService;
    private final MerchantOfficeRemote merchantOfficeRemote;
    private final IClerkAccountRoleMapService clerkAccountRoleMapService;
    private final IClerkResourceService clerkResourceService;
    private final IClerkRoleService clerkRoleService;
    private final RedisService<String, Long> longRedisService;
    private final RedisService<String, Integer> intRedisService;
    private final MerchantCashierRemote merchantCashierRemote;

    @Lazy
    public ClerkAccountServiceImpl(IMerchantService merchantService, IMerchantStoreService merchantStoreService, MerchantOfficeRemote merchantOfficeRemote, IClerkAccountRoleMapService clerkAccountRoleMapService, IClerkResourceService clerkResourceService, IClerkRoleService clerkRoleService, RedisService<String, Long> longRedisService, RedisService<String, Integer> intRedisService, MerchantCashierRemote merchantCashierRemote) {
        this.merchantService = merchantService;
        this.merchantStoreService = merchantStoreService;
        this.merchantOfficeRemote = merchantOfficeRemote;
        this.clerkAccountRoleMapService = clerkAccountRoleMapService;
        this.clerkResourceService = clerkResourceService;
        this.clerkRoleService = clerkRoleService;
        this.longRedisService = longRedisService;
        this.intRedisService = intRedisService;
        this.merchantCashierRemote = merchantCashierRemote;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String register(long merchantNo, long storeNo, String phone, String openId) {
        ClerkRole role = clerkRoleService.findByDefaultStatus(0, RoleDefaultConst.MERCHANT_DEFAULT);
        if (role == null) {
            log.error("商户默认角色错误，null,商户注册失败");
            throw new CustomException("系统异常，请稍后重试");
        }
        AccountDto accountDto = new AccountDto();
        accountDto.setRoleId(role.getId());
        accountDto.setStoreNo(storeNo); // 注册默认选中当前店铺
        accountDto.setStoreNos(Collections.singletonList(storeNo)); // 账号下有多少个店铺
        accountDto.setName("商户管理员");
        accountDto.setPhone(phone);
        accountDto.setOpenId(openId);
        return save(merchantNo, accountDto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String save(long merchantNo, AccountDto dto) {
        boolean existAccount = existByPhone(dto.getPhone());
        if (existAccount) {
            throw new CustomException(ResultCode.BAD_REQUEST, "手机号已有注册");
        }
        ClerkRole role = clerkRoleService.getById(dto.getRoleId());
        if (role == null) {
            throw new CustomException(ResultCode.BAD_REQUEST, "职位信息错误");
        }
        String rawPassword = SecurityUtil.encryptPassword(dto.getPassword());
        ClerkAccount entity = new ClerkAccount();
        BeanUtils.copyProperties(dto, entity);
        entity.setMerchantNo(merchantNo);
        entity.setStoreNos(dto.getStoreNos());
        entity.setId(IdUtil.generateLower());
        entity.setUsername(dto.getPhone());
        entity.setPassword(rawPassword);
        entity.setMobileCashMode(AccountCashMode.SCAN_MODE.getCode());
        entity.setOpenId(dto.getOpenId());
        entity.setStatus(CommonStatus.ENABLED.getCode());
        entity.setCreateTime(LocalDateTime.now());
        entity.setUpdateTime(LocalDateTime.now());
        super.save(entity);

        ClerkAccountRoleMapDto userRole = new ClerkAccountRoleMapDto();
        userRole.setAccountId(entity.getId());
        userRole.setRoleId(role.getId());
        clerkAccountRoleMapService.save(userRole);
        return entity.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(String id, AccountDto dto) {
        ClerkAccount entity = super.getById(id);
        if (entity == null) {
            throw new CustomException(ResultCode.BAD_REQUEST, "店员信息错误");
        }
        entity.setName(dto.getName());
        entity.setUsername(dto.getPhone());
        entity.setPhone(dto.getPhone());
        entity.setStoreNo(dto.getStoreNo());
        entity.setUpdateTime(LocalDateTime.now());
        ClerkAccount tempAccount = findByPhone(entity.getPhone());
        if (tempAccount != null && !tempAccount.getId().equals(id)) {
            throw new CustomException(ResultCode.BAD_REQUEST, "手机号已有注册");
        }
        super.updateById(entity);

        ClerkRole role = clerkRoleService.getById(dto.getRoleId());
        if (role == null) {
            throw new CustomException("设置权限错误");
        }
        clerkAccountRoleMapService.deleteByAccountId(entity.getId());
        ClerkAccountRoleMapDto userRole = new ClerkAccountRoleMapDto();
        userRole.setAccountId(entity.getId());
        userRole.setRoleId(role.getId());
        clerkAccountRoleMapService.save(userRole);
    }

    /**
     * 创建门店初始化店员归属门店和角色
     *
     * @param id
     * @param storeNo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editStoreNo(String id, long storeNo, String roleId) {
        if (StrUtil.isBlank(roleId)) {
            ClerkAccountRoleMap accountRole = clerkAccountRoleMapService.findByAccountId(id);
            if (accountRole != null) {
                roleId = accountRole.getRoleId();
            }
        }
        ClerkAccount entity = new ClerkAccount();
        entity.setId(id);
        entity.setStoreNo(storeNo);
        super.updateById(entity);
        clerkAccountRoleMapService.deleteByAccountId(id);
        ClerkAccountRoleMapDto accountRole = new ClerkAccountRoleMapDto();
        accountRole.setAccountId(id);
        accountRole.setRoleId(roleId);
        clerkAccountRoleMapService.save(accountRole);
    }

    @Override
    public void editName(String id, String name) {
        ClerkAccount entity = new ClerkAccount();
        entity.setId(id);
        entity.setName(name);
        super.updateById(entity);
    }

    @Override
    public void editPhone(String id, String originalPhone, String newPhone) {
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getId, id)
                .eq(ClerkAccount::getPhone, originalPhone);
        ClerkAccount entity = new ClerkAccount();
        entity.setPhone(newPhone);
        super.update(entity, wrapper);
    }

    @Override
    public void editOpenId(String id, String openId) {
        ClerkAccount entity = new ClerkAccount();
        entity.setId(id);
        entity.setOpenId(openId);
        super.updateById(entity);
    }

    @Override
    public void editStatus(String id, int status) {
        if (IEnum.getByCode(status, CommonStatus.class) == null) {
            throw new CustomException("状态信息错误");
        }
        ClerkAccount entity = new ClerkAccount();
        entity.setId(id);
        entity.setStatus(status);
        super.updateById(entity);
    }

    @Override
    public void editPassword(String id, String newPassword) {
        ClerkAccount account = super.getById(id);
        if (account == null) {
            throw new CustomException("账号信息错误");
        }
        String password = SecurityUtil.encryptPassword(newPassword);
        editPassword(id, account.getPassword(), password);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void initAccount(long merchantNo, long storeNo, String accountId, FirstSetPasswordDto dto) {
        if (StrUtil.isNotBlank(dto.getMerchantName())) {
            merchantService.editMerchantName(merchantNo, dto.getMerchantName());
            merchantStoreService.editName(merchantNo, storeNo, dto.getMerchantName());
        }
        if (StrUtil.isNotBlank(dto.getName())) {
            editName(accountId, dto.getName());
        }
        if (!dto.getConfirmPassword().equals(dto.getPassword())) {
            throw new CustomException("两次密码输入不一致");
        }
        String password = SecurityUtil.encryptPassword(dto.getPassword());
        editPassword(accountId, null, password);
    }

    @Override
    public void editPassword(String id, String originalPassword, String newPassword) {
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getId, id)
                .eq(ClerkAccount::getPassword, originalPassword);
        ClerkAccount entity = new ClerkAccount();
        entity.setPassword(newPassword);
        super.update(entity, wrapper);
    }

    @Override
    public void editMobileCashMode(String id, int cashMode) {
        ClerkAccount entity = new ClerkAccount();
        entity.setId(id);
        entity.setMobileCashMode(cashMode);
        super.updateById(entity);
    }

    @Override
    public void delete(String id) {
        ClerkAccount account = super.getById(id);
        if (account == null) {
            throw new CustomException("店员信息错误");
        }
        Merchant merchant = merchantService.findByMerchantNo(account.getMerchantNo());
        if (merchant != null && merchant.getAccountId().equals(id)) {
            throw new CustomException("商户管理人员不可删除");
        }
        List<MerchantStore> stores = merchantStoreService.findAllByMerchantNo(account.getMerchantNo());
        stores.stream().filter(x -> x.getAccountId().equals(id))
                .findFirst().ifPresent(x -> {
                    throw new CustomException(String.format("店员为(%s)门店管理人员不可删除", x.getStoreName()));
                });
        super.removeById(id);
    }

    @Override
    public UserVo info(String id) {
        ClerkAccount account = super.getById(id);
        if (account == null) {
            return null;
        }
        return convertUser(account);
    }

    @Override
    public boolean existByPhone(String phone) {
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getPhone, phone);
        return super.exists(wrapper);
    }


    /**
     * 切换店铺
     *
     * @param merchantNo   账号所属商户
     * @param checkStoreNo 切换成当前店铺
     */
    @Override
    public void checkStore(long merchantNo, String id, long checkStoreNo) {
        ClerkAccount entity = new ClerkAccount();
        entity.setId(id);
        entity.setStoreNo(checkStoreNo);
        super.updateById(entity);
    }

    @Override
    public ClerkAccount findByPhone(String phone) {
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getPhone, phone);
        return super.getOne(wrapper);
    }

    @Override
    public ClerkAccount findByOpenId(String openId) {
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getOpenId, openId);
        return super.getOne(wrapper);
    }

    @Override
    public PageVo<UserVo> page(long merchantNo,
                               long storeNo,
                               String accountId,
                               AccountQuery query) {
        MerchantStoreVo storeVo = merchantStoreService.findByStoreNo(merchantNo, storeNo);
        if (storeVo == null) {
            return PageVo.result();
        }

        if (!storeVo.isMain() || !accountId.equals(storeVo.getAccountId()) || query.getStoreNo() == null) {
            query.setStoreNo(storeNo);
        }

        if (query.isLookMy()) {
            query.setStoreNo(null);
            query.setId(accountId);
        }

        Page<ClerkAccount> tempQuery = new Page<>(query.getPageIndex(), query.getPageSize());
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getMerchantNo, merchantNo)
                .eq(StrUtil.isNotBlank(query.getId()), ClerkAccount::getId, query.getId())
                .eq(query.getStoreNo() != null, ClerkAccount::getStoreNo, query.getStoreNo())
                .and(StrUtil.isNotBlank(query.getSearchKey()),
                        name -> name
                                .like(ClerkAccount::getName, query.getSearchKey())
                                .or()
                                .like(ClerkAccount::getPhone, query.getSearchKey())
                )
                .eq(query.getStatus() != null, ClerkAccount::getStatus, query.getStatus())
                .orderByDesc(ClerkAccount::getCreateTime);
        IPage<ClerkAccount> page = super.page(tempQuery, wrapper);
        List<UserVo> vos = page.getRecords().stream().map(this::convertUser).collect(Collectors.toList());
        return PageVo.result(page, vos);
    }

    @Override
    public List<UserVo> list(long merchantNo,
                             long storeNo,
                             String accountId,
                             AccountQuery query) {
        MerchantStoreVo storeVo = merchantStoreService.findByStoreNo(merchantNo, storeNo);
        if (storeVo == null) {
            return Collections.emptyList();
        }
        if (!storeVo.isMain() || !accountId.equals(storeVo.getAccountId()) || query.getStoreNo() == null) {
            query.setStoreNo(storeNo);
        }

        if (query.isLookMy()) {
            query.setStoreNo(null);
            query.setId(accountId);
        }
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getMerchantNo, merchantNo)
                .eq(StrUtil.isNotBlank(query.getId()), ClerkAccount::getId, query.getId())
                .eq(query.getStoreNo() != null, ClerkAccount::getStoreNo, query.getStoreNo())
                .and(StrUtil.isNotBlank(query.getSearchKey()), name -> name
                        .like(ClerkAccount::getName, query.getSearchKey())
                        .or()
                        .like(ClerkAccount::getPhone, query.getSearchKey())
                )
                .eq(query.getStatus() != null, ClerkAccount::getStatus, query.getStatus())
                .orderByDesc(ClerkAccount::getCreateTime);
        List<ClerkAccount> list = super.list(wrapper);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        return list.stream().map(this::convertUser).collect(Collectors.toList());
    }

    private UserVo convertUser(ClerkAccount account) {
        UserVo vo = new UserVo();
        BeanUtils.copyProperties(account, vo);
        RoleVo role = new RoleVo();
        ClerkAccountRoleMap accountRoleMapEntity = clerkAccountRoleMapService.findByAccountId(account.getId());
        if (accountRoleMapEntity != null) {
            ClerkRole roleEntity = clerkRoleService.getById(accountRoleMapEntity.getRoleId());
            BeanUtils.copyProperties(roleEntity, role);
            vo.setRoleId(role.getId());
            vo.setRole(role);
        }
        Merchant merchant = merchantService.findByMerchantNo(account.getMerchantNo());
        if (merchant != null) {
            vo.setMerchantName(merchant.getMerchantName());
        }

        List<MerchantStoreVo> stores = merchantStoreService.findAllByStoreNos(account.getMerchantNo(), account.getStoreNos());

        // 当前门店
        MerchantStoreVo storeVo = merchantStoreService.findByStoreNo(account.getMerchantNo(), account.getStoreNo());
        if (storeVo != null) {
            vo.setStoreName(storeVo.getStoreName());
            if (account.getId().equals(storeVo.getAccountId())) {
                vo.setStoreMain(true);
            }
        }
        vo.setStores(stores);
        CashierModeVo cashierMode = merchantCashierRemote.getCashierMode(account.getMerchantNo(), account.getStoreNo(), account.getId());
        if (cashierMode != null) {
            vo.setMobileCashMode(cashierMode.getMobileMode());
            vo.setMobileCashModeName(cashierMode.getMobileModeName());
            vo.setPcCashMode(cashierMode.getPcMode());
            vo.setPcCashModeName(cashierMode.getPcModeName());
        }
        return vo;
    }

    @Override
    public List<UserVo> findAllByStoreNo(long merchantNo, long storeNo) {
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getMerchantNo, merchantNo)
                .eq(ClerkAccount::getStoreNo, storeNo);
        List<ClerkAccount> accounts = super.list(wrapper);
        return accounts.stream().map(this::convertUser).collect(Collectors.toList());
    }

    @Override
    public List<UserVo> findAll(long merchantNo) {
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getMerchantNo, merchantNo);
        List<ClerkAccount> accounts = super.list(wrapper);
        return accounts.stream().map(this::convertUser).collect(Collectors.toList());
    }

    @Override
    public List<UserVo> findAllByIds(long merchantNo, List<String> accountIds) {
        if (CollUtil.isEmpty(accountIds)) {
            return Collections.emptyList();
        }
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getMerchantNo, merchantNo)
                .in(ClerkAccount::getId, accountIds);
        List<ClerkAccount> accounts = super.list(wrapper);
        return accounts.stream().map(this::convertUser).collect(Collectors.toList());
    }

    @Override
    public List<UserVo> accountSearch(long merchantNo, String searchKey) {
        Wrapper<ClerkAccount> wrapper = new LambdaQueryWrapper<ClerkAccount>()
                .eq(ClerkAccount::getMerchantNo, merchantNo)
                .and(StrUtil.isNotBlank(searchKey),
                        name -> name
                                .like(ClerkAccount::getName, searchKey)
                                .or()
                                .like(ClerkAccount::getPhone, searchKey)
                );
        List<ClerkAccount> accounts = super.list(wrapper);
        return accounts.stream().map(this::convertUser).collect(Collectors.toList());
    }

    @Override
    public AccountDetailVo detail(long merchantNo,
                                  long storeNo,
                                  String accountId) {
        MerchantVo merchantVo = merchantService.merchantInfo(merchantNo);
        MerchantStoreVo storeVo = merchantStoreService.findByStoreNo(merchantNo, storeNo);
        List<ResourceVo> resources = clerkResourceService.findAllByAccountId(accountId);
        List<IndustryAttrVo> industryAttrs = merchantOfficeRemote.getMerchantIndustryAbility(merchantNo);
        UserVo userVo = info(accountId);
        AccountDetailVo vo = new AccountDetailVo();
        vo.setMerchantVo(merchantVo);
        vo.setStoreVo(storeVo);
        vo.setUserVo(userVo);
        vo.setResources(resources);
        vo.setIndustryAttrs(industryAttrs);
        return vo;
    }
}
